package com.hikviison.rpq.quartz.quartz.config;

import com.hikviison.rpq.quartz.quartz.task.TaskA;
import com.hikviison.rpq.quartz.quartz.task.TaskB;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * @author renpeiqian
 * @date 2021/9/14 11:01
 * @since
 */
@Configuration
public class QuartzConfiguration {


    /**
     * Quartz是一个完全由java编写的开源作业调度框架。
     * 在开发Quartz相关应用时，只要定义了Job（任务），Trigger（触发器）和Scheduler（调度器），即可实现一个定时调度能力。
     * 其中Scheduler是Quartz中的核心，Scheduler负责管理Quartz应用运行时环境，
     * Scheduler不是靠自己完成所有的工作，是根据Trigger的触发标准，调用Job中的任务执行逻辑，来完成完整的定时任务调度。
     * Job - 定时任务内容是什么。
     * Trigger - 在什么时间上执行job。
     * Scheduler - 维护定时任务环境，并让触发器生效。
     */




    /**
     * 创建Job对象。在Spring环境中，创建一个类型的对象的时候，很多情况下，都是通过FactoryBean来间接创建的。
     * 如果有多个Job对象，定义多次方法。
     *
     * 在JobDetailFactoryBean类型中，用于创建JobDetail对象的方法，其底层使用的逻辑是：Class.newInstance()
     * 也就是说，JobDetail对象不是通过Spring容器管理的。
     * 因为Spring容器不管理JobDetail对象，那么Job中需要自动装配的属性，就无法实现自动状态。如上JOB的第10行会报空指针异常。
     *
     * 解决方案是：将JobDetail加入到Spring容器中，让Spring容器管理JobDetail对象。
     * 需要重写Factory相关代码。实现Spring容器管理JobDetail。
     * @return
    */
    @Bean(name = "taskA")
    public JobDetailFactoryBean taskA(){
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
        factoryBean.setJobClass(TaskA.class);
        factoryBean.setGroup("ExpertInfo");
        factoryBean.setName("pullAndPush");
        return factoryBean;
    }

    @Bean(name = "taskB")
    public JobDetailFactoryBean taskB(){
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
        factoryBean.setJobClass(TaskB.class);
        return factoryBean;
    }




    /**
     * 管理Trigger对象
     * CronTrigger - 就是Trigger的一个实现类型。其中用于定义周期时间的是CronSchedulerBuilder
     * 实际上，CronTrigger是用于管理一个Cron表达式的类型。
     * @return
     */
    @Bean(name="taskATrigger")
    public CronTriggerFactoryBean taskTriggerForA(){
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        JobDetailFactoryBean factoryBean= this.taskA();
        //执行什么任务
        cronTriggerFactoryBean.setJobDetail(factoryBean.getObject());

        //定义cron 表达式
        cronTriggerFactoryBean.setCronExpression("0/3 * * * * ?");
        return cronTriggerFactoryBean;
    }

    @Bean(name="taskBTrigger")
    public CronTriggerFactoryBean taskTriggerForB(){
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        JobDetailFactoryBean factoryBean= this.taskB();
        //执行什么任务
        cronTriggerFactoryBean.setJobDetail(factoryBean.getObject());

        //定义cron 表达式
        cronTriggerFactoryBean.setCronExpression("0/3 * * * * ?");
        return cronTriggerFactoryBean;
    }



    /**
     * 初始化Scheduler
     * @param cronTriggerFactoryBean - 上一个方法初始化的CronTriggerFactoryBean
     * @return
     */
    @Bean
    public SchedulerFactoryBean initSchedulerFactoryBean(CustomJobFactory customJobFactory, CronTriggerFactoryBean[] cronTriggerFactoryBean){
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
        CronTrigger[] triggers = new CronTrigger[cronTriggerFactoryBean.length];
        for(int i = 0; i < cronTriggerFactoryBean.length; i++){
            triggers[i] = cronTriggerFactoryBean[i].getObject();
        }
        // 注册触发器，一个Scheduler可以注册若干触发器。
        factoryBean.setTriggers(triggers);
        // 为Scheduler设置JobDetail的工厂。可以覆盖掉SpringBoot提供的默认工厂，保证JobDetail中的自动装配有效。
        factoryBean.setJobFactory(customJobFactory);
        return factoryBean;
    }


}
